package org.biubiu0002.baiblog.core.service.impl;

import org.apache.commons.lang3.StringUtils;
import org.biubiu0002.baiblog.common.constants.CacheConstants;
import org.biubiu0002.baiblog.common.constants.FeedConstants;
import org.biubiu0002.baiblog.common.exception.BaiBlogException;
import org.biubiu0002.baiblog.common.exception.BizCodeEnumException;
import org.biubiu0002.baiblog.core.model.dto.FeedRXDto;
import org.biubiu0002.baiblog.core.model.dto.TokenUserDto;
import org.biubiu0002.baiblog.core.model.entity.ShareEntity;
import org.biubiu0002.baiblog.core.model.vo.ShareVo;
import org.biubiu0002.baiblog.core.model.vo.req.FeedReqVo;
import org.biubiu0002.baiblog.core.model.vo.res.FeedResVo;
import org.biubiu0002.baiblog.core.service.FeedService;
import org.biubiu0002.baiblog.core.service.FriendshipService;
import org.biubiu0002.baiblog.core.service.ShareService;
import org.biubiu0002.baiblog.core.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: biubiu0002
 * @Date: 2022/8/2 18:14
 **/
@Service
public class FeedServiceImpl implements FeedService {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Resource
    private FriendshipService friendshipService;

    @Resource
    private ShareService shareService;

    @Override
    public FeedResVo all(FeedReqVo feedReqVo) {
        TokenUserDto user = SecurityUtils.getUser();
        if (user == null) {
            throw new BaiBlogException(BizCodeEnumException.AUTHENTICATE_FAIL_EXCEPTION);
        }
        String uid = user.getUserUid();
        String userFeedRXKey = CacheConstants.getUserFeedRXKey(uid);
        if (!Boolean.TRUE.equals(redisTemplate.hasKey(userFeedRXKey))) {
            //加载三个月内的推文
            List<ShareEntity> shareEntities = loadFeedFromDB(uid);
            if (CollectionUtils.isEmpty(shareEntities)) {
                //关注的人都没发推文 没有动态
                FeedResVo feedResVo = new FeedResVo();
                feedResVo.setItems(Collections.emptyList());
                return feedResVo;
            }
            //当前页最后一篇推文的发布时间
            long offset = shareEntities.get(shareEntities.size() - 1).getCreateTime().getTime();
            List<String> shareIdList = shareEntities.stream().map(ShareEntity::getResourceId).collect(Collectors.toList());
            List<FeedRXDto> feedRXDtoList = shareEntities.stream().map(item -> {
                FeedRXDto feedRXDto = new FeedRXDto();
                feedRXDto.setShareId(item.getResourceId());
                feedRXDto.setTimestamp(item.getCreateTime().getTime());
                return feedRXDto;
            }).collect(Collectors.toList());
            //设置缓存到 feed信箱中
            Set<ZSetOperations.TypedTuple<String>> set = feedRXDtoList.stream().map(item -> {
                return new DefaultTypedTuple<>(item.getShareId(), item.getTimestamp().doubleValue());
            }).collect(Collectors.toSet());
            redisTemplate.opsForZSet().add(CacheConstants.getUserFeedRXKey(uid), set);
            redisTemplate.opsForZSet().removeRange(userFeedRXKey, FeedConstants.MAX_SIZE, -1);
            redisTemplate.expire(userFeedRXKey, 31, TimeUnit.DAYS);
            List<ShareVo> shareVoList = shareService.getItemsByShareIdList(shareIdList);
            FeedResVo feedResVo = new FeedResVo();
            feedResVo.setItems(shareVoList);
            feedResVo.setOffset(offset);
            return feedResVo;
        } else {
            //缓存命中 直接读缓存
            redisTemplate.expire(userFeedRXKey, 31, TimeUnit.DAYS);
            String lastIdStr = feedReqVo.getLastId();
            //传入lastId
            if (StringUtils.isNotBlank(lastIdStr)) {
                double lastId = Double.parseDouble(lastIdStr);
                //判断lastId数量 还存不存在 不存在则offset 0 存在则offset 1
                Set<String> lastIds = redisTemplate.opsForZSet().reverseRangeByScore(userFeedRXKey, lastId, lastId);
                if (lastIds == null) {
                    throw new BaiBlogException(BizCodeEnumException.REDIS_NETWORK_ERROR);
                }
                int size = lastIds.size();
                Set<ZSetOperations.TypedTuple<String>> tuples = redisTemplate.opsForZSet().reverseRangeByScoreWithScores(userFeedRXKey, 0, lastId,
                        size != 0 ? 1 : 0, FeedConstants.pageSize);
                if (tuples == null) {
                    throw new BaiBlogException(BizCodeEnumException.REDIS_NETWORK_ERROR);
                }
                return handleTuples(tuples);
            }
            //没有传入lastId
            else {
                Set<ZSetOperations.TypedTuple<String>> tuples = redisTemplate.opsForZSet().reverseRangeByScoreWithScores(userFeedRXKey, 0, System.currentTimeMillis(), 0, FeedConstants.pageSize);
                if (tuples == null) {
                    throw new BaiBlogException(BizCodeEnumException.REDIS_NETWORK_ERROR);
                }
                return handleTuples(tuples);
            }
        }
    }

    @Override
    public void sendToFans(String userUid, String shareId, Long createTime) {
        List<String> fansList = friendshipService.getFansList(userUid);
        if (CollectionUtils.isEmpty(fansList)) {
            return;
        }

        for (String fanUid : fansList) {
            /**
             * 粉丝过滤逻辑
             * key存在时 才推送到收件箱中，key不存在 等待读的时候初始化。
             */
            String userFeedRXKey = CacheConstants.getUserFeedRXKey(fanUid);
            if (Boolean.TRUE.equals(redisTemplate.hasKey(userFeedRXKey))) {
                redisTemplate.opsForZSet().add(userFeedRXKey, shareId, createTime);
                redisTemplate.opsForZSet().removeRange(userFeedRXKey, FeedConstants.MAX_SIZE, -1);
            }
        }
    }

    @Override
    public void handleFollow(String uid, String followUid, Integer isFollow) {
        if (isFollow == 1) {
            Boolean hasKey = redisTemplate.hasKey(CacheConstants.getUserFeedRXKey(uid));
            if(hasKey==null){
                throw new BaiBlogException(BizCodeEnumException.REDIS_NETWORK_ERROR);
            }
            if (Boolean.TRUE.equals(hasKey)) {
                //新关注用户 读用户最近范围内的帖子
                List<ShareEntity> shareEntities = loadFeedFromDB(followUid);
                Set<ZSetOperations.TypedTuple<String>> tuples = shareEntities.stream().map(item -> {
                    return new DefaultTypedTuple<>(item.getResourceId(), (double) item.getCreateTime().getTime());
                }).collect(Collectors.toSet());
                redisTemplate.opsForZSet().add(CacheConstants.getUserFeedRXKey(uid), tuples);
            }


        } else if (isFollow == 0) {
            //当前用户 取关用户 feedRX中 排除掉 用户的全量shareIdList
            List<ShareEntity> shareListByUserUid = shareService.getShareListByUserUidAll(followUid);
            redisTemplate.opsForZSet().remove(CacheConstants.getUserFeedRXKey(uid), shareListByUserUid.stream().map(ShareEntity::getResourceId).toArray());
        }
    }


    private FeedResVo handleTuples(Set<ZSetOperations.TypedTuple<String>> tuples) {
        if (CollectionUtils.isEmpty(tuples)) {
            FeedResVo feedResVo = new FeedResVo();
            feedResVo.setItems(Collections.emptyList());
            return feedResVo;
        }
        List<FeedRXDto> feedRXDtos = tuples.stream().map(item -> {
            FeedRXDto feedRXDto = new FeedRXDto();
            String shareId = item.getValue();
            Double score = item.getScore();
            feedRXDto.setShareId(shareId);
            assert score != null;
            feedRXDto.setTimestamp(score.longValue());
            return feedRXDto;
        }).collect(Collectors.toList());
        List<String> shareIds = feedRXDtos.stream().map(FeedRXDto::getShareId).collect(Collectors.toList());

        List<ShareVo> shareVoList = shareService.getItemsByShareIdList(shareIds);
        FeedResVo feedResVo = new FeedResVo();
        feedResVo.setItems(shareVoList);
        if(!CollectionUtils.isEmpty(shareVoList)){
            Long lastIdRes = feedRXDtos.get(feedRXDtos.size() - 1).getTimestamp();
            feedResVo.setOffset(lastIdRes);

        }

        return feedResVo;
    }

    //加载时间范围内的推文
    public List<ShareEntity> loadFeedFromDB(String uid) {
        List<String> followList = friendshipService.getFollowList(uid);
        if (CollectionUtils.isEmpty(followList)) {
            throw new BaiBlogException(BizCodeEnumException.NO_FOLLOWS_EXCEPTION);
        }
        List<ShareEntity> shareEntities = shareService.loadFeedByFollowsByCreateTime(followList, FeedConstants.LATEST_FEED_INTERVAL);
        if (CollectionUtils.isEmpty(shareEntities)) {
            return Collections.emptyList();
        }
        return shareEntities;


    }
}
